import { ElMessage, ElMessageBox } from 'element-plus'
import type { ElMessageBoxOptions } from 'element-plus'

export function transfOptionsToMap<
  Options extends Readonly<Array<{ [K in 'label' | 'value']: string | number }>>
>(
  options: Options
): { [K in Options[number]['value']]: Options[number]['label'] }
// eslint-disable-next-line no-redeclare
export function transfOptionsToMap<
  Options extends Readonly<Array<{ [K: string]: string | number }>>,
  LabelKey extends keyof Options[number],
  ValueKey extends keyof Options[number]
>(
  options: Options,
  labelKey: LabelKey,
  valueKey: ValueKey
): { [K in Options[number][ValueKey]]: Options[number][LabelKey] }
/**
 * @description 将options转换成value->key映射对象
 * @param {*} options 选项
 * @param {*} labelKey label字段
 * @param {*} valueKey value字段
 * @returns {*} value->key映射对象
 */
// eslint-disable-next-line no-redeclare
export function transfOptionsToMap(
  options:
    | Readonly<Array<{ [K: string]: string | number }>>
    | Readonly<Array<{ label: string | number, value: string | number }>>,
  labelKey?: string,
  valueKey?: string
) {
  const currentMap: Record<string, any> = {}
  for (const o of options) {
    // 使用类型断言确保 o 为具有字符串索引签名的对象
    const option = o as { [key: string]: string | number }
    const value = option[valueKey || 'value'].toString()
    const label = option[labelKey || 'label'].toString()
    currentMap[value] = label
  }
  return currentMap
}
/**
 * @description 将value->key映射对象转换成options
 * @param {*} mapObject value->key映射对象
 * @returns {*} options
 */
export function transfMapToOptions<T extends object>(mapObject: T) {
  const options: Array<{
    label: T[keyof T]
    value: keyof T
  }> = []
  for (const key in mapObject) {
    options.push({
      label: mapObject[key],
      value: key
    })
  }
  return options
}

/**
 * 弹出确认提示框，并在用户确认后执行回调函数。
 * @param callback - 用户确认后需要执行的回调函数，该函数必须返回一个 Promise。
 * @param options - 可选参数对象，包含以下可选字段：
 *   - msg: 确认框显示的消息，默认为“是否确认删除该数据?”。
 *   - hasMessage: 是否在操作成功或失败后显示消息，默认为 true。
 *   - successMsg: 操作成功时显示的消息，默认为“删除成功”。
 *   - errorMsg: 操作失败时显示的消息，默认为“删除失败”。
 *   - 其他任何 `ElMessageBoxOptions` 支持的选项。
 */
export function confirmTip<T extends () => Promise<void>>(
  callback: T,
  options: ElMessageBoxOptions & {
    msg?: string
    hasMessage?: boolean
    successMsg?: string
    errorMsg?: string
  } = {}
) {
  const defaultOptions = {
    msg: '是否确认删除该数据?',
    title: '删除确认',
    confirmButtonText: '删除',
    cancelButtonText: '取消',
    type: 'warning',
    hasMessage: true,
    successMsg: '删除成功',
    errorMsg: '删除失败'
  }
  const { msg, hasMessage, successMsg, errorMsg, ...elementMessageBoxOptions } = {
    ...defaultOptions,
    ...options
  }
  ElMessageBox.confirm(msg, elementMessageBoxOptions as ElMessageBoxOptions)
    .then(async () => {
      try {
        await callback()
        hasMessage &&
        successMsg &&
        ElMessage({
          type: 'success',
          message: successMsg
        })
      } catch {
        hasMessage &&
        errorMsg &&
        ElMessage({
          type: 'error',
          message: errorMsg
        })
      }
    })
    .catch(() => {
      // 取消删除
    })
}

/**
 * @description 等待异步函数执行完成
 * @param promise 异步函数
 * @returns 异步函数执行结果
 */
export const awaitWrap = async <T, E = Error>(
  promise: Promise<T>
): Promise<[E | null, T | null]> => {
  try {
    const data = await promise
    return [null, data]
  } catch (error: any) {
    return [error, null]
  }
}

/**
 * @description 等待异步函数执行完成，并返回格式化后的结果，适用于简化API请求操作
 * @param promise 异步函数
 * @returns 格式化后的结果
 * @example const [error, data] = await awaitAPIWrapWithFormat(promise)
 */
export const awaitAPIWrapWithFormat = async <T extends { toString(): string }>(
  promise: Promise<{ success: boolean, data: T, msg: string }>
): Promise<[Error | null, T | null]> => {
  try {
    const { success, data, msg } = await promise
    return success ? [null, data] : [data ? new Error(data.toString()) : new Error(msg), null]
  } catch (error) {
    return [error as Error, null]
  }
}
